﻿using System;
using System.Threading.Tasks;
using IOP.Models.Message.MQTT;
using IOP.Models.Message.MQTT.Package;
using IOP.Pulsar.Abstractions;
using IOP.Pulsar.MQTT.Abstractions;
using IOP.Pulsar.MQTT.Result;
using IOP.Extension;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Reflection;

namespace IOP.Pulsar.MQTT
{
    /// <summary>
    /// MQTT控制中心中间件
    /// </summary>
    public class MQTTControlCenterMiddleware : IProductLineMiddleware<MQTTContext>
    {
        /// <summary>
        /// 路由
        /// </summary>
        private readonly IControllerRouter _Router;
        /// <summary>
        /// 主题中心
        /// </summary>
        private readonly ITopicCenter _Center;
        /// <summary>
        /// 日志
        /// </summary>
        private readonly ILogger<MQTTControlCenterMiddleware> _Logger;
        /// <summary>
        /// 服务提供者
        /// </summary>
        private readonly IServiceProvider _ServiceProvider;
        private readonly Type DefaultContollerType = typeof(MQTTController);

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="router">控制器路由</param>
        /// <param name="provider">服务提供者</param>
        /// <param name="center">主题中心</param>
        /// <param name="logger">日志</param>
        public MQTTControlCenterMiddleware(IControllerRouter router, IServiceProvider provider, ILogger<MQTTControlCenterMiddleware> logger, ITopicCenter center)
        {
            _Router = router;
            _ServiceProvider = provider;
            _Logger = logger;
            _Center = center;
        }

        /// <summary>
        /// 执行
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public async Task InvokeAsync(MQTTContext context, ProductLineDelegate<MQTTContext> next)
        {
            try
            {
                MQTTResult result = null;
                switch (context.Request.Package)
                {
                    case ConnectPackage connect:
                        result = ConnectResult(connect, context);
                        break;
                    case PublishPackage publish:
                        result = PublishResult(publish, context);
                        break;
                    case PubackPackage puback:
                        result = PubackResult(puback, context);
                        break;
                    case PubrecPackage pubrec:
                        result = PubrecResult(pubrec, context);
                        break;
                    case PubrelPackage pubrel:
                        result = PubrelResult(pubrel, context);
                        break;
                    case PubcompPackage pubcomp:
                        result = PubcompResult(pubcomp, context);
                        break;
                    case SubscribePackage subscribe:
                        result = SubscribeResult(subscribe, context);
                        break;
                    case UnsubscribePackage unsubscribe:
                        result = UnsubscribeResult(unsubscribe, context);
                        break;
                    case PingreqPackage pingreq:
                        result = PingreqResult(pingreq, context);
                        break;
                    case DisconnectPackage disconnect:
                        result = Disconnect(disconnect, context);
                        break;
                    default:
                        throw new Exception($"Package Type Error, The Package Type is {context.Request.Package.PacketType}");
                }
                await next(context);
                result.ExcuteResult(context);
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + e.StackTrace);
                context.Response.SendAndClose(new DisconnectPackage(0));
            }
        }

        /// <summary>
        /// 连接回执
        /// </summary>
        /// <param name="connect"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTResult ConnectResult(ConnectPackage connect, MQTTContext context)
        {
            MQTTResult result = null;
            var method = _Router.GetControllerMethod(MQTTConstant.Connect);
            if (method != null)
            {
                var param = method.GetParameters();
                object[] paramers = new object[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    if (param[i].ParameterType == typeof(ConnectPackage)) paramers[i] = connect;
                    else paramers[i] = new object();
                }
                result = GetControllerResult(method.DeclaringType, method, context, paramers);
            }
            else
            {
                var defaultController = GetDefaultController(context);
                result = defaultController.Connack(ReturnCodeType.Accept);
            }
            return result;
        }

        /// <summary>
        /// 部署结果
        /// </summary>
        /// <param name="publish"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTResult PublishResult(PublishPackage publish, MQTTContext context)
        {
            MQTTResult result = null;
            var method = _Router.GetControllerMethod(publish.TopicName);
            if(method != null)
            {
                var param = method.GetParameters();
                object[] paramers = new object[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    if (param[i].ParameterType == typeof(PublishPackage)) paramers[i] = publish;
                    else paramers[i] = new object();
                }
                result = GetControllerResult(method.DeclaringType, method, context, paramers);
            }
            else
            {
                var defaultController = GetDefaultController(context);
                result = defaultController.PublishFinish();
            }
            return result;
        }

        /// <summary>
        /// 发布回执
        /// </summary>
        /// <param name="puback"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTResult PubackResult(PubackPackage puback, MQTTContext context)
        {
            MQTTResult result = null;
            var method = _Router.GetControllerMethod(MQTTConstant.Puback);
            if (method != null)
            {
                var param = method.GetParameters();
                object[] paramers = new object[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    if (param[i].ParameterType == typeof(PubackPackage)) paramers[i] = puback;
                    else paramers[i] = new object();
                }
                result = GetControllerResult(method.DeclaringType, method, context, paramers);
            }
            else
            {
                var defaultController = GetDefaultController(context);
                result = defaultController.Puback();
            }
            return result;
        }

        /// <summary>
        /// 发布收到
        /// </summary>
        /// <param name="pubrec"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTResult PubrecResult(PubrecPackage pubrec, MQTTContext context)
        {
            MQTTResult result = null;
            var method = _Router.GetControllerMethod(MQTTConstant.Pubrec);
            if (method != null)
            {
                var param = method.GetParameters();
                object[] paramers = new object[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    if (param[i].ParameterType == typeof(PubrecPackage)) paramers[i] = pubrec;
                    else paramers[i] = new object();
                }
                result = GetControllerResult(method.DeclaringType, method, context, paramers);
            }
            else
            {
                var defaultController = GetDefaultController(context);
                result = defaultController.Pubrec();
            }
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pubrel"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTResult PubrelResult(PubrelPackage pubrel, MQTTContext context)
        {
            MQTTResult result = null;
            var method = _Router.GetControllerMethod(MQTTConstant.Pubrel);
            if (method != null)
            {
                var param = method.GetParameters();
                object[] paramers = new object[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    if (param[i].ParameterType == typeof(PubrelPackage)) paramers[i] = pubrel;
                    else paramers[i] = new object();
                }
                result = GetControllerResult(method.DeclaringType, method, context, paramers);
            }
            else
            {
                var defaultController = GetDefaultController(context);
                result = defaultController.Pubrel();
            }
            return result;
        }

        /// <summary>
        /// 发布完成
        /// </summary>
        /// <param name="pubcomp"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTResult PubcompResult(PubcompPackage pubcomp, MQTTContext context)
        {
            MQTTResult result = null;
            var method = _Router.GetControllerMethod(MQTTConstant.Pubcomp);
            if (method != null)
            {
                var param = method.GetParameters();
                object[] paramers = new object[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    if (param[i].ParameterType == typeof(PubcompPackage)) paramers[i] = pubcomp;
                    else paramers[i] = new object();
                }
                result = GetControllerResult(method.DeclaringType, method, context, paramers);
            }
            else
            {
                var defaultController = GetDefaultController(context);
                result = defaultController.Pubcomp();
            }
            return result;
        }

        /// <summary>
        /// 订阅结果
        /// </summary>
        /// <param name="subscribe"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTResult SubscribeResult(SubscribePackage subscribe, MQTTContext context)
        {
            MQTTResult result = null;
            var method = _Router.GetControllerMethod(MQTTConstant.Subscribe);
            if (method != null)
            {
                var param = method.GetParameters();
                object[] paramers = new object[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    if (param[i].ParameterType == typeof(SubscribePackage)) paramers[i] = subscribe;
                    else paramers[i] = new object();
                }
                result = GetControllerResult(method.DeclaringType, method, context, paramers);
            }
            else
            {
                var defaultController = GetDefaultController(context);
                result = defaultController.Suback();
            }
            return result;
        }

        /// <summary>
        /// 取消订阅结果
        /// </summary>
        /// <param name="unsubscribe"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTResult UnsubscribeResult(UnsubscribePackage unsubscribe, MQTTContext context)
        {
            MQTTResult result = null;
            var method = _Router.GetControllerMethod(MQTTConstant.Subscribe);
            if (method != null)
            {
                var param = method.GetParameters();
                object[] paramers = new object[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    if (param[i].ParameterType == typeof(UnsubscribePackage)) paramers[i] = unsubscribe;
                    else paramers[i] = new object();
                }
                result = GetControllerResult(method.DeclaringType, method, context, paramers);
            }
            else
            {
                var defaultController = GetDefaultController(context);
                result = defaultController.Unsuback();
            }
            return result;
        }

        /// <summary>
        /// 心跳请求
        /// </summary>
        /// <param name="pingreq"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTResult PingreqResult(PingreqPackage pingreq, MQTTContext context)
        {
            MQTTResult result = null;
            var method = _Router.GetControllerMethod(MQTTConstant.Pingreq);
            if (method != null)
            {
                var param = method.GetParameters();
                object[] paramers = new object[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    if (param[i].ParameterType == typeof(PingreqPackage)) paramers[i] = pingreq;
                    else paramers[i] = new object();
                }
                result = GetControllerResult(method.DeclaringType, method, context, paramers);
            }
            else
            {
                var defaultController = GetDefaultController(context);
                result = defaultController.Pingresp();
            }
            return result;
        }

        /// <summary>
        /// 断开连接请求
        /// </summary>
        /// <param name="package"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTResult Disconnect(DisconnectPackage package, MQTTContext context)
        {
            MQTTResult result = null;
            var method = _Router.GetControllerMethod(MQTTConstant.Disconnet);
            if (method != null)
            {
                var param = method.GetParameters();
                object[] paramers = new object[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    if (param[i].ParameterType == typeof(DisconnectPackage)) paramers[i] = package;
                    else paramers[i] = new object();
                }
                result = GetControllerResult(method.DeclaringType, method, context, paramers);
            }
            else
            {
                var defaultController = GetDefaultController(context);
                result = defaultController.Disconnect();
            }
            return result;
        }

        /// <summary>
        /// 获取一个控制器方法
        /// </summary>
        /// <param name="controllerType"></param>
        /// <param name="context"></param>
        /// <param name="method"></param>
        /// <param name="paramers"></param>
        /// <returns></returns>
        private MQTTResult GetControllerResult(Type controllerType, MethodInfo method, MQTTContext context, object[] paramers)
        {
            MQTTResult result = null;
            var loop = _Router.Controllers[controllerType];
            if (loop.IsEmpty)
            {
                var controller = ActivatorUtilities.CreateInstance(_ServiceProvider, controllerType) as MQTTController;
                controller.SetPropertyValue("Context", context);
                controller.SetPropertyValue("TopicCenter", _Center);
                result = method.Invoke(controller, paramers) as MQTTResult;
                loop.Enqueue(controller);
            }
            else
            {
                loop.TryDequeue(out MQTTController controller);
                if (controller == null)
                {
                    controller = ActivatorUtilities.CreateInstance(_ServiceProvider, controllerType) as MQTTController;
                    controller.SetPropertyValue("Context", context);
                    controller.SetPropertyValue("TopicCenter", _Center);
                }
                else controller.SetPropertyValue("Context", context);
                result = method.Invoke(controller, paramers) as MQTTResult;
                loop.Enqueue(controller);
            }
            return result;
        }

        /// <summary>
        /// 获取默认控制器
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private MQTTController GetDefaultController(MQTTContext context)
        {
            MQTTController controller = null;
            var loop = _Router.Controllers[DefaultContollerType];
            if (loop.IsEmpty)
            {
                controller = new MQTTController();
                controller.SetPropertyValue("Context", context);
                controller.SetPropertyValue("TopicCenter", _Center);
                loop.Enqueue(controller);
            }
            else
            {
                loop.TryDequeue(out controller);
                if (controller == null)
                {
                    controller = new MQTTController();
                    controller.SetPropertyValue("Context", context);
                    controller.SetPropertyValue("TopicCenter", _Center);
                }
                else controller.SetPropertyValue("Context", context);
                loop.Enqueue(controller);
            }
            return controller;
        }
    }
}
